package com.dgsrz.demo.game;

import android.graphics.PointF;
import android.renderscript.Matrix4f;
import com.dgsrz.demo.primitive.TexturedCircle;
import com.dgsrz.demo.primitive.TexturedRectangle;
import com.dgsrz.demo.util.Spline;

import javax.microedition.khronos.opengles.GL10;
import java.util.ArrayList;
import java.util.LinkedList;

/**
 * Created by dgsrz on 15/7/3.
 */
public class Slider extends HitObjectBase {

    private static final int ANIM_SNAKE_LENGTH = 300;
    private static final int CIRCLE_SIZE = 64;

    private Spline.CurveTypes mCurveType;

    private LinkedList<ArrayList<PointF>> mControlPoints;
    private ArrayList<PointF> mSavedPathPoints;

    private TexturedRectangle mRectangle;
    private TexturedCircle mCircle;

    private float mAlpha;
    private long mAnimStartTime;

    public Slider(final GL10 gl) {
        super(gl);

        mCurveType = Spline.CurveTypes.Bezier;
        mControlPoints = new LinkedList<>();
        mSavedPathPoints = null;

        mRectangle = new TexturedRectangle(gl);
        mCircle = new TexturedCircle(gl);

        mAnimStartTime = 0;
        mAlpha = 1f;
    }

    public ArrayList<PointF> getPathPoints() {
        if (mSavedPathPoints == null) {
            mSavedPathPoints = new ArrayList<>();
            for (ArrayList<PointF> points : mControlPoints) {
                mSavedPathPoints.addAll(new Spline(points, mCurveType).getPoints());
            }
        }
        return mSavedPathPoints;
    }

    @Override
    public void drawObject() {
        final GL10 gl = getGLStateManager();

        ArrayList<PointF> points = getPathPoints();
        int lastIndex = points.size() - 1;

        long currentMillis = System.currentTimeMillis();
        if (currentMillis - mAnimStartTime < ANIM_SNAKE_LENGTH) {
            float animPercent = 1.f * (currentMillis - mAnimStartTime) / ANIM_SNAKE_LENGTH;
            lastIndex = (int)(lastIndex * animPercent);
            setAlpha(animPercent);
        } else {
            setAlpha(1f);
        }

        float circleScale = CIRCLE_SIZE / 2f;
        Matrix4f matrix = new Matrix4f();

        // Draw slider head
        {
            PointF current = points.get(0);
            PointF next = points.get(1);

            float x1 = current.x;
            float x2 = next.x;
            float y1 = current.y;
            float y2 = next.y;

            double theta = Math.atan2(y1 - y2, x1 - x2);
            float sine = (float) Math.sin(theta);
            float cosine = (float) Math.cos(theta);

            gl.glPushMatrix();
            matrix.loadIdentity();
            matrix.set(0, 0, cosine);
            matrix.set(0, 1, sine);
            matrix.set(1, 0, -sine);
            matrix.set(1, 1, cosine);
            matrix.set(3, 0, x1);
            matrix.set(3, 1, y1);
            gl.glLoadMatrixf(matrix.getArray(), 0);
            mCircle.drawPrimitives();
            gl.glPopMatrix();
        }

        // Draw slider body
        for (int i = 0; i < lastIndex; i++) {
            PointF current = points.get(i);
            PointF next = points.get(i + 1);

            float x1 = current.x;
            float x2 = next.x;
            float y1 = current.y;
            float y2 = next.y;

            float distance = (float) Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
            float magic = circleScale / distance;

            double theta = Math.atan2(y2 - y1, x2 - x1);
            float sine = (float) Math.sin(theta);
            float cosine = (float) Math.cos(theta);

            // Rotate the world
            gl.glPushMatrix();
            matrix.loadIdentity();
            matrix.set(0, 0, x2 - x1);
            matrix.set(0, 1, y2 - y1);
            matrix.set(1, 0, -magic * (y2 - y1));
            matrix.set(1, 1, magic * (x2 - x1));
            matrix.set(3, 0, x1);
            matrix.set(3, 1, y1);
            gl.glLoadMatrixf(matrix.getArray(), 0);
            mRectangle.drawPrimitives();
            gl.glPopMatrix();

            gl.glPushMatrix();
            matrix.loadIdentity();
            matrix.set(0, 0, cosine);
            matrix.set(0, 1, sine);
            matrix.set(1, 0, -sine);
            matrix.set(1, 1, cosine);
            matrix.set(3, 0, x2);
            matrix.set(3, 1, y2);
            gl.glLoadMatrixf(matrix.getArray(), 0);
            mCircle.drawPrimitives();
            gl.glPopMatrix();
        }
    }

    public void beginAnimation() {
        mAnimStartTime = System.currentTimeMillis();
    }

    public void setCurveType(Spline.CurveTypes curveType) {
        this.mCurveType = curveType;
    }

    public void setControlPoints(LinkedList<ArrayList<PointF>> controlPoints) {
        this.mControlPoints = controlPoints;
    }

    public final float getAlpha() {
        return mAlpha;
    }

    public void setAlpha(float alpha) {
        this.mAlpha = alpha;
    }
}
